#ifndef __SERIALIZE__
#define __SERIALIZE__

namespace boost {
	namespace serialization {
		//////////////////////////////////////////////////////////////////////////////////////////////////////////	
		template<class Archive, typename K, unsigned N>
		void serialize(Archive& ar, ::math::Vec<K, N>& v, const unsigned int version) {
        		using namespace boost::serialization;
			for(size_t i = 0; i < N; ++i) {
				std::string name = "a" + boost::lexical_cast<std::string>(i);
				ar & make_nvp(name.c_str(), v[i]);
			}
		}

		template<class T, unsigned SIZE>
		struct implementation_level< ::math::Vec<T, SIZE> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<object_serializable> type;
			BOOST_STATIC_CONSTANT(int, value = implementation_level::type::value);
		};

		template<class T, unsigned SIZE>
		struct tracking_level< ::math::Vec<T, SIZE> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<track_never> type;
			BOOST_STATIC_CONSTANT(int, value = tracking_level::type::value);
		};
		//////////////////////////////////////////////////////////////////////////////////////////////////////////	
		template<class Archive>
		void serialize(Archive& ar, std::map<unsigned, Vec9n>& entry, const unsigned int version) {
			if(Archive::is_loading::value) {
				unsigned n;
				ar & make_nvp("n", n);
				unsigned i = 0;
				for(unsigned k = 0; k < n; k++) {
					std::pair<unsigned, Vec9n> t;
					std::string name = "tensor" + boost::lexical_cast<std::string>(i++);
					ar & make_nvp(name.c_str(), t);
					entry[t.first] = t.second;
				}
			} else {
				unsigned i = 0;
				size_t n = entry.size();
				ar & make_nvp("n", n);
				for(std::map<unsigned, Vec9n>::iterator it = entry.begin(); it != entry.end(); it++) {
					std::pair<unsigned, Vec9n> p = static_cast< std::pair<unsigned, Vec9n> >(*it);
					std::string name = "tensor" + boost::lexical_cast<std::string>(i++);
					ar & make_nvp(name.c_str(), p);
				}
			}
		}
		template<>
		struct implementation_level< std::map<unsigned, Vec9n> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<object_serializable> type;
			BOOST_STATIC_CONSTANT(int, value = implementation_level::type::value);
		};

		template<>
		struct tracking_level< std::map<unsigned, Vec9n> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<track_never> type;
			BOOST_STATIC_CONSTANT(int, value = tracking_level::type::value);
		};
		//////////////////////////////////////////////////////////////////////////////////////////////////////////	
		template<class Archive>
		void serialize(Archive& ar, boost::tuples::tuple<unsigned, unsigned, nfloat>& t, const unsigned int version) {
        		using namespace boost::serialization;
			ar & make_nvp("i", boost::tuples::get<0>(t));
			ar & make_nvp("j", boost::tuples::get<1>(t));
			ar & make_nvp("d", boost::tuples::get<2>(t));
		}
		
		template<>	
		struct implementation_level<boost::tuples::tuple<unsigned, unsigned, nfloat> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<object_serializable> type;
			BOOST_STATIC_CONSTANT(int, value = implementation_level::type::value);
		};
		
		template<>
		struct tracking_level<boost::tuples::tuple<unsigned, unsigned, nfloat> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<track_never> type;
			BOOST_STATIC_CONSTANT(int, value = tracking_level::type::value);
		};
		//////////////////////////////////////////////////////////////////////////////////////////////////////////	
		template<class Archive>
		void serialize(Archive& ar, std::pair<unsigned, Vec9n>& p, const unsigned int version) {
        		using namespace boost::serialization;
			ar & make_nvp("i", p.first);
			ar & make_nvp("e", p.second);
		}
		
		template<>
		struct implementation_level< std::pair<unsigned, Vec9n> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<object_serializable> type;
			BOOST_STATIC_CONSTANT(int, value = implementation_level::type::value);
		};
		
		template<>
		struct tracking_level< std::pair<unsigned, Vec9n> > {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<track_never> type;
			BOOST_STATIC_CONSTANT(int, value = tracking_level::type::value);
		};
		//////////////////////////////////////////////////////////////////////////////////////////////////////////	
		template<class Archive>
		void serialize(Archive& ar, SparseMatrix& A, const unsigned int version) {
			if(Archive::is_loading::value) {
				unsigned n, m, e;
				
				ar & make_nvp("n", n);
				ar & make_nvp("m", m);
				ar & make_nvp("k", e);

				A = SparseMatrix(n, m, e);
				for(unsigned k = 0; k < e; k++) {
					boost::tuples::tuple<unsigned, unsigned, nfloat> t;
					ar & make_nvp("e", t);
					unsigned i = boost::tuples::get<0>(t);
					unsigned j = boost::tuples::get<1>(t);
					nfloat d = boost::tuples::get<2>(t);
					A(i, j) = d;
				}
			} else {
				typedef boost::numeric::ublas::matrix_row< SparseMatrix > Row;
				std::vector< boost::tuples::tuple<unsigned, unsigned, nfloat> > entry;

				unsigned count = 0;
				for(size_t i = 0; i < A.size1(); i++) {
					Row row(A, i);
					for(Row::iterator itr = row.begin(); itr != row.end(); itr++) {
						unsigned j = itr.index();
						entry.push_back(boost::tuples::tuple<unsigned, unsigned, nfloat>(i, j, A(i, j)));
						count++;
					}
				}
				
				unsigned n = A.size1();
				unsigned m = A.size2();
				unsigned e = entry.size();
				ar & make_nvp("n", n);
				ar & make_nvp("m", m);
				ar & make_nvp("k", e);
				for(size_t i = 0; i < entry.size(); i++) {
					boost::tuples::tuple<unsigned, unsigned, nfloat>& t = entry[i];
					ar & make_nvp("e", t);
				}
			}
		}
		
		template<>	
		struct implementation_level<SparseMatrix> {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<object_serializable> type;
			BOOST_STATIC_CONSTANT(int, value = implementation_level::type::value);
		};

		template<>	
		struct tracking_level<SparseMatrix> {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<track_never> type;
			BOOST_STATIC_CONSTANT(int, value = tracking_level::type::value);
		};
		//////////////////////////////////////////////////////////////////////////////////////////////////////////	
		template<class Archive>
		void serialize(Archive& ar, Vector& v, const unsigned int version) {
			if(Archive::is_loading::value) {
				unsigned n;
				
				ar & make_nvp("n", n);

				v = Vector(n);
				for(unsigned i = 0; i < n; i++)
					ar & make_nvp("e", v(i));
			} else {
				unsigned n = v.size();
				ar & make_nvp("n", n);
				for(size_t i = 0; i < n; i++)
					ar & make_nvp("e", v(i));
			}
		}
		
		template<>	
		struct implementation_level<Vector> {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<object_serializable> type;
			BOOST_STATIC_CONSTANT(int, value = implementation_level::type::value);
		};

		template<>	
		struct tracking_level<Vector> {
			typedef mpl::integral_c_tag tag;
			typedef mpl::int_<track_never> type;
			BOOST_STATIC_CONSTANT(int, value = tracking_level::type::value);
		};

	}
}

#endif //__SERIALIZE__
